package com.xiaomaoguai.fcp.pre.kepler.router.rpc.httpconverter;

import java.io.InputStream;
import java.lang.reflect.InvocationHandler;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URI;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.TreeSet;
import java.util.UUID;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;

import org.objenesis.strategy.StdInstantiatorStrategy;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.ByteBufferInput;
import com.esotericsoftware.kryo.io.ByteBufferOutput;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.esotericsoftware.kryo.serializers.DefaultSerializers;

import de.javakaffee.kryoserializers.ArraysAsListSerializer;
import de.javakaffee.kryoserializers.BitSetSerializer;
import de.javakaffee.kryoserializers.CollectionsEmptyListSerializer;
import de.javakaffee.kryoserializers.CollectionsEmptyMapSerializer;
import de.javakaffee.kryoserializers.CollectionsEmptySetSerializer;
import de.javakaffee.kryoserializers.CollectionsSingletonListSerializer;
import de.javakaffee.kryoserializers.CollectionsSingletonMapSerializer;
import de.javakaffee.kryoserializers.CollectionsSingletonSetSerializer;
import de.javakaffee.kryoserializers.GregorianCalendarSerializer;
import de.javakaffee.kryoserializers.JdkProxySerializer;
import de.javakaffee.kryoserializers.RegexSerializer;
import de.javakaffee.kryoserializers.SynchronizedCollectionsSerializer;
import de.javakaffee.kryoserializers.URISerializer;
import de.javakaffee.kryoserializers.UUIDSerializer;
import de.javakaffee.kryoserializers.UnmodifiableCollectionsSerializer;
import de.javakaffee.kryoserializers.cglib.CGLibProxySerializer;
import de.javakaffee.kryoserializers.dexx.ListSerializer;
import de.javakaffee.kryoserializers.dexx.MapSerializer;
import de.javakaffee.kryoserializers.dexx.SetSerializer;
import de.javakaffee.kryoserializers.guava.ArrayListMultimapSerializer;
import de.javakaffee.kryoserializers.guava.ArrayTableSerializer;
import de.javakaffee.kryoserializers.guava.HashBasedTableSerializer;
import de.javakaffee.kryoserializers.guava.HashMultimapSerializer;
import de.javakaffee.kryoserializers.guava.ImmutableListSerializer;
import de.javakaffee.kryoserializers.guava.ImmutableMapSerializer;
import de.javakaffee.kryoserializers.guava.ImmutableMultimapSerializer;
import de.javakaffee.kryoserializers.guava.ImmutableSetSerializer;
import de.javakaffee.kryoserializers.guava.ImmutableTableSerializer;
import de.javakaffee.kryoserializers.guava.LinkedHashMultimapSerializer;
import de.javakaffee.kryoserializers.guava.LinkedListMultimapSerializer;
import de.javakaffee.kryoserializers.guava.ReverseListSerializer;
import de.javakaffee.kryoserializers.guava.TreeBasedTableSerializer;
import de.javakaffee.kryoserializers.guava.TreeMultimapSerializer;
import de.javakaffee.kryoserializers.guava.UnmodifiableNavigableSetSerializer;

public class KryoSerializer implements Serializer {

	public static final String CONTENT_TYPE = "application/kepler-kryo;charset=UTF-8";

	private ThreadLocal<Kryo> kryos = new ThreadLocal<Kryo>() {
		@Override
		protected Kryo initialValue() {
			Kryo kryo = new Kryo();
			kryo.setRegistrationRequired(false);
			kryo.setReferences(true);
			kryo.register(Arrays.asList("").getClass(), new ArraysAsListSerializer());
			kryo.register(GregorianCalendar.class, new GregorianCalendarSerializer());
			kryo.register(InvocationHandler.class, new JdkProxySerializer());
			kryo.register(BigDecimal.class, new DefaultSerializers.BigDecimalSerializer());
			kryo.register(BigInteger.class, new DefaultSerializers.BigIntegerSerializer());
			kryo.register(Pattern.class, new RegexSerializer());
			kryo.register(BitSet.class, new BitSetSerializer());
			kryo.register(URI.class, new URISerializer());
			kryo.register(UUID.class, new UUIDSerializer());
			UnmodifiableCollectionsSerializer.registerSerializers(kryo);
			SynchronizedCollectionsSerializer.registerSerializers(kryo);
			kryo.register(HashMap.class);
			kryo.register(ArrayList.class);
			kryo.register(LinkedList.class);
			kryo.register(HashSet.class);
			kryo.register(TreeSet.class);
			kryo.register(Hashtable.class);
			kryo.register(Date.class);
			kryo.register(Calendar.class);
			kryo.register(ConcurrentHashMap.class);
			kryo.register(SimpleDateFormat.class);
			kryo.register(GregorianCalendar.class);
			kryo.register(Vector.class);
			kryo.register(BitSet.class);
			kryo.register(StringBuffer.class);
			kryo.register(StringBuilder.class);
			kryo.register(Object.class);
			kryo.register(Object[].class);
			kryo.register(String[].class);
			kryo.register(byte[].class);
			kryo.register(char[].class);
			kryo.register(int[].class);
			kryo.register(float[].class);
			kryo.register(double[].class);
			
			kryo.register( Collections.EMPTY_LIST.getClass(), new CollectionsEmptyListSerializer() );
			kryo.register( Collections.EMPTY_MAP.getClass(), new CollectionsEmptyMapSerializer() );
			kryo.register( Collections.EMPTY_SET.getClass(), new CollectionsEmptySetSerializer() );
			kryo.register( Collections.singletonList( "" ).getClass(), new CollectionsSingletonListSerializer() );
			kryo.register( Collections.singleton( "" ).getClass(), new CollectionsSingletonSetSerializer() );
			kryo.register( Collections.singletonMap( "", "" ).getClass(), new CollectionsSingletonMapSerializer() );
			kryo.register( CGLibProxySerializer.CGLibProxyMarker.class, new CGLibProxySerializer(  ) );
			ListSerializer.registerSerializers( kryo );
			MapSerializer.registerSerializers( kryo );
			SetSerializer.registerSerializers( kryo );
			// guava ImmutableList, ImmutableSet, ImmutableMap, ImmutableMultimap, ImmutableTable, ReverseList, UnmodifiableNavigableSet
			ImmutableListSerializer.registerSerializers( kryo );
			ImmutableSetSerializer.registerSerializers( kryo );
			ImmutableMapSerializer.registerSerializers( kryo );
			ImmutableMultimapSerializer.registerSerializers( kryo );
			ImmutableTableSerializer.registerSerializers( kryo );
			ReverseListSerializer.registerSerializers( kryo );
			UnmodifiableNavigableSetSerializer.registerSerializers( kryo );
			// guava ArrayListMultimap, HashMultimap, LinkedHashMultimap, LinkedListMultimap, TreeMultimap, ArrayTable, HashBasedTable, TreeBasedTable
			ArrayListMultimapSerializer.registerSerializers( kryo );
			HashMultimapSerializer.registerSerializers( kryo );
			LinkedHashMultimapSerializer.registerSerializers( kryo );
			LinkedListMultimapSerializer.registerSerializers( kryo );
			TreeMultimapSerializer.registerSerializers( kryo );
			ArrayTableSerializer.registerSerializers( kryo );
			HashBasedTableSerializer.registerSerializers( kryo );
			TreeBasedTableSerializer.registerSerializers( kryo );
			kryo.setInstantiatorStrategy(new Kryo.DefaultInstantiatorStrategy(new StdInstantiatorStrategy()));
			return kryo;
		}
	};
	@Override
	public byte[] writeObject(Object obj) {
		Kryo kryo = kryos.get();
		Output output = null;
		try {
			output = new ByteBufferOutput(4096, 1024 * 1024);
			kryo.writeClassAndObject(output, obj);
			return output.toBytes();
		} finally {
			if (output != null)
				output.close();
		}
	}
	@Override
	public Object readObject(byte[] bytes) {
		Kryo kryo = kryos.get();
		Input input = null;
		try {
			input = new ByteBufferInput(bytes);
			Object inputObj = kryo.readClassAndObject(input);
			return inputObj;
		} finally {
			if (input != null)
				input.close();
		}
	}
	@Override
	public Object readObject(InputStream inputStream) {
		Kryo kryo = kryos.get();
		Input input = null;
		try {
			input = new ByteBufferInput(inputStream);
			Object inputObj = kryo.readClassAndObject(input);
			return inputObj;
		} finally {
			if (input != null)
				input.close();
		}
	}
}
